package cn.iocoder.yudao.module.erp.service.purchaseplan;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;
import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum;
import cn.iocoder.yudao.module.erp.controller.admin.purchaseplan.vo.PurchasePlanPageReqVO;
import cn.iocoder.yudao.module.erp.controller.admin.purchaseplan.vo.PurchasePlanRespVO;
import cn.iocoder.yudao.module.erp.controller.admin.purchaseplan.vo.PurchasePlanSaveReqVO;
import cn.iocoder.yudao.module.erp.controller.admin.purchaseplan.vo.purchaseengineeringdetail.PurchaseEngineeringDetailExportRespVO;
import cn.iocoder.yudao.module.erp.controller.admin.purchaseplan.vo.purchaseengineeringdetail.PurchaseEngineeringDetailExportVO;
import cn.iocoder.yudao.module.erp.controller.admin.purchaseplan.vo.purchaseengineeringdetail.PurchaseEngineeringDetailRespVO;
import cn.iocoder.yudao.module.erp.controller.admin.purchaseplan.vo.purchaseengineeringdetail.PurchaseEngineeringDetailSaveReqVO;
import cn.iocoder.yudao.module.erp.controller.admin.purchaseplan.vo.purchaseequipmentdetail.PurchaseEquipmentDetailExportRespVO;
import cn.iocoder.yudao.module.erp.controller.admin.purchaseplan.vo.purchaseequipmentdetail.PurchaseEquipmentDetailExportVO;
import cn.iocoder.yudao.module.erp.controller.admin.purchaseplan.vo.purchaseequipmentdetail.PurchaseEquipmentDetailRespVO;
import cn.iocoder.yudao.module.erp.controller.admin.purchaseplan.vo.purchaseequipmentdetail.PurchaseEquipmentDetailSaveReqVO;
import cn.iocoder.yudao.module.erp.controller.admin.purchaseplan.vo.purchasematerialdetail.PurchaseMaterialDetailExportRespVO;
import cn.iocoder.yudao.module.erp.controller.admin.purchaseplan.vo.purchasematerialdetail.PurchaseMaterialDetailExportVO;
import cn.iocoder.yudao.module.erp.controller.admin.purchaseplan.vo.purchasematerialdetail.PurchaseMaterialDetailRespVO;
import cn.iocoder.yudao.module.erp.controller.admin.purchaseplan.vo.purchasematerialdetail.PurchaseMaterialDetailSaveReqVO;
import cn.iocoder.yudao.module.erp.controller.admin.purchaseplan.vo.purchaseminordetail.PurchaseMinorDetailExportRespVO;
import cn.iocoder.yudao.module.erp.controller.admin.purchaseplan.vo.purchaseminordetail.PurchaseMinorDetailExportVO;
import cn.iocoder.yudao.module.erp.controller.admin.purchaseplan.vo.purchaseminordetail.PurchaseMinorDetailRespVO;
import cn.iocoder.yudao.module.erp.controller.admin.purchaseplan.vo.purchaseminordetail.PurchaseMinorDetailSaveReqVO;
import cn.iocoder.yudao.module.erp.controller.admin.purchaseplan.vo.purchaseservicedetail.PurchaseServiceDetailExportRespVO;
import cn.iocoder.yudao.module.erp.controller.admin.purchaseplan.vo.purchaseservicedetail.PurchaseServiceDetailExportVO;
import cn.iocoder.yudao.module.erp.controller.admin.purchaseplan.vo.purchaseservicedetail.PurchaseServiceDetailRespVO;
import cn.iocoder.yudao.module.erp.controller.admin.purchaseplan.vo.purchaseservicedetail.PurchaseServiceDetailSaveReqVO;
import cn.iocoder.yudao.module.erp.dal.dataobject.purchaseplan.PurchasePlanDO;
import cn.iocoder.yudao.module.erp.dal.mysql.purchaseplan.PurchasePlanMapper;
import cn.iocoder.yudao.module.erp.enums.PurchaseTypeConstants;
import cn.iocoder.yudao.module.erp.service.purchaseplan.purchaseengineeringdetail.PurchaseEngineeringDetailService;
import cn.iocoder.yudao.module.erp.service.purchaseplan.purchaseequipmentdetail.PurchaseEquipmentDetailService;
import cn.iocoder.yudao.module.erp.service.purchaseplan.purchasematerialdetail.PurchaseMaterialDetailService;
import cn.iocoder.yudao.module.erp.service.purchaseplan.purchaseminordetail.PurchaseMinorDetailService;
import cn.iocoder.yudao.module.erp.service.purchaseplan.purchaseservicedetail.PurchaseServiceDetailService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.PURCHASE_PLAN_NOT_EXISTS;

/**
 * 采购计划 Service 实现类
 *
 * @author 芋道源码
 */
@Service
@Validated
public class PurchasePlanServiceImpl implements PurchasePlanService {

    @Resource
    private PurchasePlanMapper purchasePlanMapper;
    @Resource
    private PurchaseServiceDetailService serviceDetailService;
    @Resource
    private PurchaseEngineeringDetailService engineeringDetailService;
    @Resource
    private PurchaseEquipmentDetailService equipmentDetailService;
    @Resource
    private PurchaseMaterialDetailService materialDetailService;
    @Resource
    private PurchaseMinorDetailService minorDetailService;
    @Resource
    private BpmProcessInstanceApi processInstanceApi;
    @Value("${task.purchase_plan.key:purchase_plan_audit}")
    private String purchasePlanTask;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createPurchasePlan(PurchasePlanSaveReqVO createReqVO) {
        // 插入主表
        String planNo = IdUtil.fastSimpleUUID();
        createReqVO.setPlanNo(planNo);
        PurchasePlanDO purchasePlan = BeanUtils.toBean(createReqVO, PurchasePlanDO.class);
        purchasePlan.setStatus(BpmProcessInstanceStatusEnum.RUNNING.getStatus());
        purchasePlanMapper.insert(purchasePlan);
        //插入不同的详情表
        createReqVO.setId(purchasePlan.getId());
        createDetailBatch(createReqVO);

        //插入工作流
        Map<String, Object> processInstanceVariables = new HashMap<>();
        processInstanceVariables.put("purchasePlanId", purchasePlan.getId());
        processInstanceVariables.put("purchaseType", createReqVO.getPurchaseType());
        processInstanceVariables.put("purchaseName", purchasePlan.getPlanName());
        processInstanceVariables.put("purchaseNo", purchasePlan.getPlanNo());
        processInstanceVariables.put("project", purchasePlan.getProjectName());
        processInstanceVariables.put("purchaseMethod", purchasePlan.getPurchaseMethod());
        processInstanceVariables.put("applicationDepartmentName", purchasePlan.getApplicationDepartmentName());
        processInstanceVariables.put("budgetDepartmentName", purchasePlan.getBudgetDepartmentName());
        processInstanceVariables.put("applicantName", purchasePlan.getApplicantName());
        String processInstanceId = processInstanceApi.createProcessInstance(getLoginUserId(),
                new BpmProcessInstanceCreateReqDTO().setProcessDefinitionKey(purchasePlanTask)
                        .setVariables(processInstanceVariables).setBusinessKey(String.valueOf(purchasePlan.getId()))
        );
        PurchasePlanDO update = new PurchasePlanDO();
        update.setId(purchasePlan.getId());
        update.setProcessInstanceId(processInstanceId);
        purchasePlanMapper.updateById(update);

        // 返回
        return purchasePlan.getId();
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updatePurchasePlan(PurchasePlanSaveReqVO updateReqVO) {
        // 校验存在
        validatePurchasePlanExists(updateReqVO.getId());
        // 更新
        PurchasePlanDO updateObj = BeanUtils.toBean(updateReqVO, PurchasePlanDO.class);
        purchasePlanMapper.updateById(updateObj);
        //删除不同的详情表
        Long planId = updateReqVO.getId();
        String purchaseType = updateReqVO.getPurchaseType();
        deleteDetailBatch(purchaseType, planId);
        //插入不同的详情表
        createDetailBatch(updateReqVO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deletePurchasePlan(Long id) {
        PurchasePlanDO purchasePlanDO = purchasePlanMapper.selectById(id);
        // 校验存在
        validatePurchasePlanExists(id);
        // 删除
        purchasePlanMapper.deleteById(id);
        // 删除明细表
        deleteDetailBatch(purchasePlanDO.getPurchaseType(), id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deletePurchasePlanListByIds(List<Long> ids) {
        ids.forEach(this::deletePurchasePlan);
    }

    private void validatePurchasePlanExists(List<Long> ids) {
        List<PurchasePlanDO> list = purchasePlanMapper.selectByIds(ids);
        if (CollUtil.isEmpty(list) || list.size() != ids.size()) {
            throw exception(PURCHASE_PLAN_NOT_EXISTS);
        }
    }

    private void validatePurchasePlanExists(Long id) {
        if (purchasePlanMapper.selectById(id) == null) {
            throw exception(PURCHASE_PLAN_NOT_EXISTS);
        }
    }

    @Override
    public PurchasePlanRespVO getPurchasePlan(Long id) {
        PurchasePlanDO purchasePlanDO = purchasePlanMapper.selectById(id);
        PurchasePlanRespVO respVO = BeanUtils.toBean(purchasePlanDO, PurchasePlanRespVO.class);

        //填充采购明细
        String purchaseType = respVO.getPurchaseType();
        PurchaseTypeConstants purchaseTypeConstant = PurchaseTypeConstants.ofByCode(purchaseType);

        switch (purchaseTypeConstant) {
            case SERVICE:
                List<PurchaseServiceDetailRespVO> serviceDetailRespVOS = BeanUtils.toBean(serviceDetailService.getByPlanId(id), PurchaseServiceDetailRespVO.class);
                respVO.setServiceDetailRespVOS(serviceDetailRespVOS);
                break;
            case ENGINEERING:
                List<PurchaseEngineeringDetailRespVO> engineeringDetailRespVOS = BeanUtils.toBean(engineeringDetailService.getByPlanId(id), PurchaseEngineeringDetailRespVO.class);
                respVO.setEngineeringDetailRespVOS(engineeringDetailRespVOS);
                break;
            case EQUIPMENT:
                List<PurchaseEquipmentDetailRespVO> equipmentDetailRespVOS = BeanUtils.toBean(equipmentDetailService.getByPlanId(id), PurchaseEquipmentDetailRespVO.class);
                respVO.setEquipmentDetailRespVOS(equipmentDetailRespVOS);
                break;
            case MATERIAL:
                List<PurchaseMaterialDetailRespVO> materialDetailRespVOS = BeanUtils.toBean(materialDetailService.getByPlanId(id), PurchaseMaterialDetailRespVO.class);
                respVO.setMaterialDetailRespVOS(materialDetailRespVOS);
                break;
            case MINOR:
            case DIRECT:
                List<PurchaseMinorDetailRespVO> minorDetailRespVOS = BeanUtils.toBean(minorDetailService.getByPlanId(id), PurchaseMinorDetailRespVO.class);
                respVO.setMinorDetailRespVOS(minorDetailRespVOS);
                break;
            default:
                throw new RuntimeException(String.format("未适配采购类型%s", purchaseTypeConstant.getCode()));
        }
        return respVO;
    }

    @Override
    public PageResult<PurchasePlanDO> getPurchasePlanPage(PurchasePlanPageReqVO pageReqVO) {
        return purchasePlanMapper.selectPage(pageReqVO);
    }

    @Override
    public PurchaseServiceDetailExportRespVO importServiceDetailList(List<PurchaseServiceDetailExportVO> serviceDetailExportVOS) {
        List<PurchaseServiceDetailExportVO> successList = new ArrayList<>();
        List<PurchaseServiceDetailExportVO> failList = new ArrayList<>();
        serviceDetailExportVOS.forEach(x->{
            String message = serviceDetailExportCheck(x);
            if (StrUtil.isBlank(message)){
                successList.add(x);
            }else {
                x.setMessage(message);
                failList.add(x);
            }
        });
        PurchaseServiceDetailExportRespVO build = PurchaseServiceDetailExportRespVO.builder()
                .successList(successList)
                .failList(failList)
                .build();
        build.setErrorMessageList();
        return build;
    }

    @Override
    public PurchaseEquipmentDetailExportRespVO importEquipmentDetailList(List<PurchaseEquipmentDetailExportVO> equipmentDetailExportVOS) {
        List<PurchaseEquipmentDetailExportVO> successList = new ArrayList<>();
        List<PurchaseEquipmentDetailExportVO> failList = new ArrayList<>();
        equipmentDetailExportVOS.forEach(x->{
            String message = equipmentDetailExportCheck(x);
            if (StrUtil.isBlank(message)){
                successList.add(x);
            }else {
                x.setMessage(message);
                failList.add(x);
            }
        });
        PurchaseEquipmentDetailExportRespVO build = PurchaseEquipmentDetailExportRespVO.builder()
                .successList(successList)
                .failList(failList)
                .build();
        build.setErrorMessageList();
        return build;
    }

    @Override
    public PurchaseEngineeringDetailExportRespVO importEngineeringDetailList(List<PurchaseEngineeringDetailExportVO> engineeringDetailExportRespVOS) {
        List<PurchaseEngineeringDetailExportVO> successList = new ArrayList<>();
        List<PurchaseEngineeringDetailExportVO> failList = new ArrayList<>();
        engineeringDetailExportRespVOS.forEach(x->{
            String message =  engineeringDetailExportCheck(x);
            if (StrUtil.isBlank(message)){
                successList.add(x);
            }else {
                x.setMessage(message);
                failList.add(x);
            }
        });
        PurchaseEngineeringDetailExportRespVO build = PurchaseEngineeringDetailExportRespVO.builder()
                .successList(successList)
                .failList(failList)
                .build();
        build.setErrorMessageList();
        return build;
    }

    @Override
    public PurchaseMaterialDetailExportRespVO importMaterialDetailList(List<PurchaseMaterialDetailExportVO> materialDetailExportVOS) {
        List<PurchaseMaterialDetailExportVO> successList = new ArrayList<>();
        List<PurchaseMaterialDetailExportVO> failList = new ArrayList<>();
        materialDetailExportVOS.forEach(x->{
            String message =  materialDetailExportCheck(x);
            if (StrUtil.isBlank(message)){
                successList.add(x);
            }else {
                x.setMessage(message);
                failList.add(x);
            }
        });
        PurchaseMaterialDetailExportRespVO build = PurchaseMaterialDetailExportRespVO.builder()
                .successList(successList)
                .failList(failList)
                .build();
        build.setErrorMessageList();
        return build;
    }

    @Override
    public PurchaseMinorDetailExportRespVO importMinorDetailList(List<PurchaseMinorDetailExportVO> minorDetailExportVOS) {
        List<PurchaseMinorDetailExportVO> successList = new ArrayList<>();
        List<PurchaseMinorDetailExportVO> failList = new ArrayList<>();
        minorDetailExportVOS.forEach(x->{
            String message =  minorDetailExportCheck(x);
            if (StrUtil.isBlank(message)){
                successList.add(x);
            }else {
                x.setMessage(message);
                failList.add(x);
            }
        });
        PurchaseMinorDetailExportRespVO build = PurchaseMinorDetailExportRespVO.builder()
                .successList(successList)
                .failList(failList)
                .build();
        build.setErrorMessageList();
        return build;
    }


    @Override
    public int updateStatus(long id, Integer status) {
        validatePurchasePlanExists(id);
        PurchasePlanDO purchaseOrder = new PurchasePlanDO();
        purchaseOrder.setId(id);
        purchaseOrder.setStatus(status);
        return purchasePlanMapper.updateById(purchaseOrder);
    }

    /**
     * 导入直接类、小额零星采购明细
     * @param minorDetailExportVO
     * @return 返回失败明细，成功返回null
     */
    private String minorDetailExportCheck(PurchaseMinorDetailExportVO minorDetailExportVO) {
        if (StrUtil.isBlank(minorDetailExportVO.getItemName())){
            return "名称不能为空";
        }
        return StrUtil.EMPTY;
    }

    /**
     * 物资采购明细导入
     * @param materialDetailExportVO
     * @return 返回失败明细，成功返回null
     */
    private String materialDetailExportCheck(PurchaseMaterialDetailExportVO materialDetailExportVO) {
        if (StrUtil.isBlank(materialDetailExportVO.getMaterialCode())){
            return "物料编码不能为空";
        }
        return StrUtil.EMPTY;
    }

    /**
     * 校驗工程采购明细导入
     * @param engineeringDetailExportVO
     * @return 返回失败明细，成功返回null
     */
    private String engineeringDetailExportCheck(PurchaseEngineeringDetailExportVO engineeringDetailExportVO) {
        if (StrUtil.isBlank(engineeringDetailExportVO.getPurchaseContent())){
            return "内容名称不能为空";
        }
        return StrUtil.EMPTY;
    }

    /**
     * 校驗设备采购明细导入
     * @param equipmentDetailExportVO
     * @return 返回失败明细，成功返回null
     */
    private String equipmentDetailExportCheck(PurchaseEquipmentDetailExportVO equipmentDetailExportVO) {
        if (StrUtil.isBlank(equipmentDetailExportVO.getEquipmentName())){
            return "设备名称不能为空";
        }
        return StrUtil.EMPTY;
    }

    /**
     * 校驗服务采购明细导入
     * @param serviceDetailExportVO
     * @return 返回失败明细，成功返回null
     */
    private String serviceDetailExportCheck(PurchaseServiceDetailExportVO serviceDetailExportVO) {
        if (StrUtil.isBlank(serviceDetailExportVO.getPurchaseContent())){
            return "内容名称不能为空";
        }
        return StrUtil.EMPTY;
    }

    private void createDetailBatch(PurchasePlanSaveReqVO createReqVO) {
        String purchaseType = createReqVO.getPurchaseType();
        PurchaseTypeConstants purchaseTypeConstant = PurchaseTypeConstants.ofByCode(purchaseType);
        Long planId = createReqVO.getId();
        String planNo = createReqVO.getPlanNo();
        switch (purchaseTypeConstant) {
            case SERVICE:
                List<PurchaseServiceDetailSaveReqVO> serviceDetailSaveReqVOS = createReqVO.getServiceDetailSaveReqVOS();
                serviceDetailSaveReqVOS.forEach(plan->{
                    plan.setPlanId(planId);
                    plan.setPlanNo(planNo);
                });
                serviceDetailService.createPurchaseServiceDetailBatch(serviceDetailSaveReqVOS);
                break;
            case ENGINEERING:
                List<PurchaseEngineeringDetailSaveReqVO> engineeringDetailSaveReqVOS = createReqVO.getEngineeringDetailSaveReqVOS();
                engineeringDetailSaveReqVOS.forEach(plan->{
                    plan.setPlanId(planId);
                    plan.setPlanNo(planNo);
                });
                engineeringDetailService.createPurchaseEngineeringDetailBatch(engineeringDetailSaveReqVOS);
                break;
            case EQUIPMENT:
                List<PurchaseEquipmentDetailSaveReqVO> equipmentDetailSaveReqVOS = createReqVO.getEquipmentDetailSaveReqVOS();
                equipmentDetailSaveReqVOS.forEach(plan->{
                    plan.setPlanId(planId);
                    plan.setPlanNo(planNo);
                });
                equipmentDetailService.createPurchaseEquipmentDetailBatch(equipmentDetailSaveReqVOS);
                break;
            case MATERIAL:
                List<PurchaseMaterialDetailSaveReqVO> materialDetailSaveReqVOS = createReqVO.getMaterialDetailSaveReqVOS();
                materialDetailSaveReqVOS.forEach(plan->{
                    plan.setPlanId(planId);
                    plan.setPlanNo(planNo);
                });
                materialDetailService.createPurchaseMaterialDetailBatch(materialDetailSaveReqVOS);
                break;
            case MINOR:
            case DIRECT:
                List<PurchaseMinorDetailSaveReqVO> directDetailSaveReqVOS = createReqVO.getMinorDetailSaveReqVOS();
                directDetailSaveReqVOS.forEach(plan->{
                    plan.setPlanId(planId);
                    plan.setPlanNo(planNo);
                });
                minorDetailService.createPurchaseMinorDetailBatch(directDetailSaveReqVOS);
                break;
            default:
                throw new RuntimeException(String.format("未适配采购类型%s", purchaseTypeConstant.getCode()));
        }
    }

    private void deleteDetailBatch(String purchaseType, Long planId) {
        PurchaseTypeConstants purchaseTypeConstant = PurchaseTypeConstants.ofByCode(purchaseType);

        switch (purchaseTypeConstant) {
            case SERVICE:
                serviceDetailService.deleteByPlanId(planId);
                break;
            case ENGINEERING:
                engineeringDetailService.deleteByPlanId(planId);
                break;
            case EQUIPMENT:
                equipmentDetailService.deleteByPlanId(planId);
                break;
            case MATERIAL:
                materialDetailService.deleteByPlanId(planId);
                break;
            case MINOR:
            case DIRECT:
                minorDetailService.deleteByPlanId(planId);
                break;
            default:
                throw new RuntimeException(String.format("未适配采购类型%s", purchaseTypeConstant.getCode()));
        }
    }

}